/*
* Copyright (c) 2012 Kyle Hayward
* 
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* 
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
* 
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#pragma once

#include "Buffer.h"

namespace OptixDotNet 
{
	ref class D3DBuffer;

	/// <summary>
	/// Description data used to create a <see cref="OptixDotNet::D3DBuffer">D3DBuffer</see>.
	/// </summary>
	public value struct D3DBufferDesc
	{
		/// <summary>
		/// Width of the Buffer.
		/// </summary>
		Size Width;

		/// <summary>
		/// Height of the Buffer.
		/// </summary>
		Size Height;

		/// <summary>
		/// Depth of the Buffer.
		/// </summary>
		Size Depth;

		/// <summary>
		/// <see cref="OptixDotNet::BufferType">Type</see> of the Buffer.
		/// </summary>
		BufferType Type;

		/// <summary>
		/// <see cref="OptixDotNet::Format">Format</see> of the Buffer.
		/// </summary>
		OptixDotNet::Format Format;

		/// <summary>
		/// Size of the Type the buffer contains. Only needed for <see cref="OptixDotNet::Format::User">Format.User</see> formats.
		/// </summary>
		Size ElemSize;

		/// <summary>
		/// Type of <see cref="OptixDotNet::D3DApi">Direct3D API</see> that will be used to create the D3DBuffer.
		/// </summary>
		D3DApi Api;

		/// <summary>
		/// Pointer to the D3D resource used to create the D3DBuffer.<br/>
		/// Valid D3D9 Resources: IDirect3DVertexBuffer9 and IDirect3DIndexBuffer9.<br/>
		/// Valid D3D10/11 Resources: ID3D10Buffer and ID3D11Buffer respectively.<br/>
		/// </summary>
		System::IntPtr Resource;

		inline static D3DBufferDesc GetDefault( D3DApi api, System::IntPtr resource )
		{
			D3DBufferDesc desc = Default;
			desc.Api = api;
			desc.Resource = resource;

			return desc;
		}

		/// <summary>
		/// Gets a default D3DBufferDesc with safe initilaized values.
		/// Width = 0
		/// Height = 0
		/// Depth = 0
		/// Type = None
		/// Format = Unknown
		/// Api = None
		/// Resource = null
		/// </summary>
		static property D3DBufferDesc Default
		{
			D3DBufferDesc get()
			{
				D3DBufferDesc desc = { 0, 0, 0, BufferType::None, OptixDotNet::Format::Unknown, 0, D3DApi::None, System::IntPtr::Zero };
				return desc;
			}
		}

		/// <summary>
		/// Gets a default D3DBufferDesc with safe initilaized values.
		/// Width = 0
		/// Height = 0
		/// Depth = 0
		/// Type = None
		/// Format = Unknown
		/// Api = Direct3D9
		/// Resource = null
		/// </summary>
		static property D3DBufferDesc DefaultD3D9
		{
			D3DBufferDesc get()
			{
				D3DBufferDesc desc = { 0, 0, 0, BufferType::None, OptixDotNet::Format::Unknown, 0, D3DApi::Direct3D9, System::IntPtr::Zero };
				return desc;
			}
		}

		/// <summary>
		/// Gets a default D3DBufferDesc with safe initilaized values.
		/// Width = 0
		/// Height = 0
		/// Depth = 0
		/// Type = None
		/// Format = Unknown
		/// Api = Direct3D10
		/// Resource = null
		/// </summary>
		static property D3DBufferDesc DefaultD3D10
		{
			D3DBufferDesc get()
			{
				D3DBufferDesc desc = { 0, 0, 0, BufferType::None, OptixDotNet::Format::Unknown, 0, D3DApi::Direct3D10, System::IntPtr::Zero };
				return desc;
			}
		}

		/// <summary>
		/// Gets a default D3DBufferDesc with safe initilaized values.
		/// Width = 0
		/// Height = 0
		/// Depth = 0
		/// Type = None
		/// Format = Unknown
		/// Api = Direct3D11
		/// Resource = null
		/// </summary>
		static property D3DBufferDesc DefaultD3D11
		{
			D3DBufferDesc get()
			{
				D3DBufferDesc desc = { 0, 0, 0, BufferType::None, OptixDotNet::Format::Unknown, 0, D3DApi::Direct3D11, System::IntPtr::Zero };
				return desc;
			}
		}
	};

	/// <summary>
	/// Represents a buffer created from a D3D resource. D3D buffer objects can be read and written to.
	/// Before a D3DBuffer object can be created, a D3D device needs to be set on the Context.
	/// <para>
	/// Valid D3D9 Resources: IDirect3DVertexBuffer9 and IDirect3DIndexBuffer9
	/// Valid D3D10/11 Resources: ID3D10Buffer and ID3D11Buffer respectively
	/// </para>
	/// <para>
	/// Once a D3DBuffer is created the D3D resource is immutable, meaning its properties cannot be modified.
	/// You must unregister the Buffer in order to modify its properties. A following Register must be called.
	/// </para>
    /// </summary>
	public ref class D3DBuffer : public Buffer
	{
	public:

		/// <summary>
		/// Create a Buffer from a D3D resource.
		/// </summary>
		/// <param name="context">Optix context</param>
		/// <param name="desc">Description of the buffer</param>
		D3DBuffer( Context^ context, D3DBufferDesc desc );

		/// <summary>
		/// Declares a D3D buffer as immutable and accessible by OptiX.
		/// Once registered, the buffer's properties will not be able to be modified
		/// </summary>
		void Register();

		/// <summary>
		/// Declares a D3D buffer as mutable and inaccessible by OptiX.
		/// Once unregistered, the buffer's properties are able to be modified.
		/// The buffer must be re-registered once changes are done being made.
		/// </summary>
		void Unregister();

		/// <summary>
		/// Gets a pointer to the underlying D3D buffer resource.
		/// </summary>
		property System::IntPtr Resource
		{
			System::IntPtr get();
		}

	private:
		void Create( D3DBufferDesc% desc );

	private:
		D3DApi mApi;
	};
}